package com.tomato.spring.framework.util;

import com.tomato.spring.framework.enums.ProtoType;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;

/**
 * 反射工具类
 *
 * @author tomato
 */
@Slf4j
public class ClassUtil {
    private static final String classFileSuffix = "class";

    /**
     * 获取对应包下的class集合
     *
     * @param packageName
     * @return
     */
    public static Set<Class<?>> extractPackageClass(String packageName) {
        // 获取类加载器
        ClassLoader classLoader = getClassLoader();

        // 获取文件包名的路径
        URL url = classLoader.getResource(packageName.replace(".", "/"));

        if (url == null) {
            log.warn(MessageFormat.format("unable to retrieve anything from package: {0}", packageName));
            return null;
        }

        Set<Class<?>> set = null;
        if (url.getProtocol().equalsIgnoreCase(ProtoType.FILE.getName())) {
            set = new HashSet<>();
            extractPackageClass(set, new File(url.getPath()), packageName);
            return set;
        }

        // TODO 这里还可以针对其他资源进行处理，例如properties解析 xml解析等
        return set;
    }

    /**
     * 递归获取packageDirectory下的Class对象
     *
     * @param fileSource
     * @param packageName
     */
    public static void extractPackageClass(Set<Class<?>> emptySet, File fileSource, String packageName) {
        if (fileSource.isFile()) {
            return;
        }

        // 过滤出遍历出所有的目录
        File[] files = fileSource.listFiles((file) -> {
            if (file.isDirectory()) {
                return true;
            }
            // 如果是文件且文件是class结尾的那么就将class文件转换为Class对象添加到load中\
            if (file.getName().endsWith(classFileSuffix)) {
                addClassToSet(emptySet, file, packageName);
            }

            return false;
        });

        if (files != null) {
            for (File file : files) {
                extractPackageClass(emptySet, file, packageName);
            }
        }
    }

    public static void addClassToSet(Set<Class<?>> emptySet, File classFile, String packageName) {
        // 获取class文件的绝对路径
        String path = classFile.getAbsolutePath();

        // e:/a/b/c/d/Hello.class
        // 将class文件的 / 替换成 .
        String replace = path.replace(File.separator, ".");

        String className = replace.substring(replace.lastIndexOf(packageName), replace.lastIndexOf("."));

        emptySet.add(loadClass(className));

    }

    public static Class<?> loadClass(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("load class exception:{}", e);
            throw new RuntimeException(e);
        }
    }

    public static ClassLoader getClassLoader() {
        // 获取类加载器
        return Thread.currentThread().getContextClassLoader();
    }

    public static <T> T newInstance(Class<?> clazz) {
        return newInstance(clazz, true);
    }


    public static <T> T newInstance(Class<?> clazz, boolean access) {
        try {
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(access);
            return (T) constructor.newInstance();
        } catch (Exception e) {
            log.error(MessageFormat.format("init {0} exception:", clazz.getName()), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 给对象属性进行赋值
     *
     * @param target
     * @param field
     * @param args
     */
    public static void setFiled(Field field, Object target, Object args, boolean access) {
        try {
            field.setAccessible(access);
            field.set(target, args);
        } catch (IllegalAccessException e) {
            log.info(MessageFormat.format("给属性 {0}#{1} 赋值时发生错误:", target.getClass().getName(), field.getName()), e);
            throw new RuntimeException(e);
        }
    }
}
